# 从 Django 的 shortcuts 模块中导入了 render 和 redirect 函数。render 函数用于渲染指定的 HTML 模板，并将相关数据传递给模板进行展示；
# redirect 函数则用于实现页面重定向，引导用户跳转到其他指定的 URL 路径。
from django.shortcuts import render,redirect
# 从名为 app 的应用下的 models 模块中导入了 User（用户模型类）和 TravelInfo（旅游信息模型类），用于后续与数据库中对应的用户表和旅游信息表进行交互
from app.models import User,TravelInfo
# 导入了 HttpResponse 类，虽然在当前代码中未直接体现其使用，但它常用于返回简单的 HTTP 响应内容
from django.http import HttpResponse
# 从 app 应用下的 recommdation 模块中导入了两个函数，这两个函数应该是用于实现基于用户的协同过滤推荐相关逻辑，
# 例如获取用户评分数据以及根据用户评分进行推荐计算等，为推荐功能提供核心算法支持。
from app.recommdation import getUser_ratings,user_bases_collaborative_filtering
# 从 app 应用下的 utils 模块中导入了多个工具函数或类，分别用于处理错误响应、获取首页相关数据、获取公共数据、修改用户自身信息相关数据处理、
# 添加评论相关数据操作、生成 Echarts 图表数据以及获取推荐相关数据等不同功能，体现了代码的模块化设计思路，
# 将不同功能的逻辑封装在不同的工具模块中便于维护和复用。
from app.utils import errorResponse,getHomeData,getPublicData,getChangeSelfInfoData,getAddCommentsData,getEchartsData,getRecommendationData
#
def login(request):
    # 首先判断请求方法，如果是 GET 请求，说明用户正在访问登录页面，直接通过 render 函数渲染 login.html 模板并返回给客户端，展示登录页面给用户。
    if request.method == 'GET':
        return render(request,'login.html')
    # 若为 POST 请求，则表示用户提交了登录表单，从请求数据中获取用户名和密码信息。
    else:
        username = request.POST.get('username')
        password = request.POST.get('password')
        # 接着使用 User.objects.get 尝试从数据库中获取匹配该用户名和密码的用户记录，如果获取成功，说明登录信息正确，
        # 将用户名存入当前会话（request.session['username']）用于后续验证用户登录状态，然后通过 redirect 函数重定向到应用下的
        # home 页面（/app/home）。
        try:
            User.objects.get(username=username, password=password)
            request.session['username'] = username
            return redirect('/app/home')
        # 若查询过程中出现异常（即没有找到匹配的用户记录），则调用 errorResponse 模块中的 errorResponse 函数返回一个
        # 包含 “账号或密码错误” 提示信息的错误响应，告知用户登录失败。
        except:
            return errorResponse.errorResponse(request,'账号或密码错误')


def register(request):
    # 当请求方法为 GET 时，渲染 register.html 模板，展示用户注册页面给用户。
    if request.method == 'GET':
        return render(request,'register.html')
    # 对于 POST 请求，先获取用户在注册表单中输入的用户名、密码以及确认密码信息。然后尝试通过 User.objects.get
    # 查询数据库中是否已存在该用户名的用户记录，若查询出现异常（即不存在该用户名），进行一系列验证：
    else:
        username = request.POST.get('username')
        password = request.POST.get('password')
        confirmPassword = request.POST.get('confirmPassword')
        try:
            User.objects.get(username=username)
        except:
            # 如果用户名、密码或确认密码有任何一个为空值，返回相应错误提示；
            if not username or not password or not confirmPassword:return errorResponse.errorResponse(request,'不允许为空值')
            # 若两次输入的密码不一致，同样返回对应错误提示。
            if  password != confirmPassword:return errorResponse.errorResponse(request,'两次密码不一致')
            User.objects.create(username=username,password=password)
            # 只有当所有验证通过后，使用 User.objects.create 在数据库中创建新的用户记录，创建成功后重定向到 login 页面（/app/login），
            # 提示用户可以进行登录操作。
            return redirect('/app/login')
        # 若在最开始查询用户名时发现已存在该账号，则返回相应错误提示告知用户账号已存在，注册失败。
        return errorResponse.errorResponse(request,'该账号已存在')
# 该函数用于处理用户注销登录的操作，通过调用 request.session.clear() 清除当前会话中的所有数据，实现注销功能，
# 然后重定向用户到 login 页面（/app/login），方便用户重新登录或者进行其他操作。
def logOut(request):
    request.session.clear()
    return redirect('/app/login')

def home(request):
    # 首先从当前会话中获取用户名，
    username = request.session.get('username')
    # 然后通过 User.objects.get 根据用户名从数据库获取对应的用户信息。
    userInfo = User.objects.get(username=username)
    # 接着调用 getHomeData 模块中的多个函数分别获取首页相关的各种数据
    a5Len,commentsLenTitle,provienceDicSort = getHomeData.getHomeTagData()
    scoreTop10Data,saleCountTop10 = getHomeData.getAnthorData()
    year,mon,day = getHomeData.getNowTime()
    geoData = getHomeData.getGeoData()
    userBarCharData = getHomeData.getUserCreateTimeData()
    # 最后将获取到的用户信息以及各类数据整理成字典形式，通过 render 函数传递给 home.html 模板进行渲染展示，
    # 构建出网站的首页内容，向用户展示各种综合信息。
    return render(request,'home.html',{
        'userInfo':userInfo,
        'a5Len':a5Len,
        'commentsLenTitle':commentsLenTitle,
        'provienceDicSort':provienceDicSort,
        'scoreTop10Data':scoreTop10Data,
        'nowTime':{
            'year':year,
            'mon':getPublicData.monthList[mon - 1],
            'day':day
        },
        'geoData':geoData,
        'userBarCharData':userBarCharData,
        'saleCountTop10':saleCountTop10
    })

def changeSelfInfo(request):
    # 先从会话中获取用户名，进而从数据库获取对应的用户信息，并获取当前时间信息。
    username = request.session.get('username')
    userInfo = User.objects.get(username=username)
    year,mon,day = getHomeData.getNowTime()
    # 当请求方法为 POST 时，表示用户提交了修改自身信息的表单，调用 getChangeSelfInfoData 模块中的 changeSelfInfo 函数，
    # 传入用户名以及包含表单数据（request.POST）和上传文件数据（request.FILES）的参数，用于处理修改用户信息的具体逻辑，
    # 比如更新数据库中用户记录的相关字段值等操作。
    if request.method == 'POST':
        getChangeSelfInfoData.changeSelfInfo(username,request.POST,request.FILES)
        userInfo = User.objects.get(username=username)
    # 修改完成后，再次从数据库获取更新后的用户信息，最后将更新后的用户信息和当前时间信息传递给 changeSelfInfo.html 模板进行渲染展示，
    # 让用户看到修改后的自身信息情况或者继续进行其他修改操作。
    return render(request,'changeSelfInfo.html',{
        'userInfo':userInfo,
        'nowTime': {
            'year': year,
            'mon': getPublicData.monthList[mon - 1],
            'day': day
        },
    })

def changePassword(request):
    # 同样先获取用户名及对应的用户信息和当前时间信息。
    username = request.session.get('username')
    userInfo = User.objects.get(username=username)
    year, mon, day = getHomeData.getNowTime()
    # 当接收到 POST 请求时，意味着用户提交了修改密码的表单，调用 getChangeSelfInfoData 模块中的 getChangePassword 函数，
    # 传入用户信息对象和表单数据，该函数应该是用于处理密码修改的验证等具体逻辑，
    if request.method == 'POST':
        res = getChangeSelfInfoData.getChangePassword(userInfo,request.POST)
        # 若函数返回不为 None 的结果，说明密码修改出现问题（可能是旧密码验证不通过等原因），则调用 errorResponse 模块的
        # errorResponse 函数返回相应错误提示信息给用户。
        if res != None:
            return errorResponse.errorResponse(request,res)
    # 若密码修改验证等环节都通过（函数返回 None），页面正常渲染 changePassword.html 模板，展示修改密码相关的页面内容给用户，
    # 用户可继续操作或者查看修改结果。
    return render(request, 'changePassword.html', {
        'userInfo': userInfo,
        'nowTime': {
            'year': year,
            'mon': getPublicData.monthList[mon - 1],
            'day': day
        },
    })

def tableData(request):
    # 从会话中获取用户名及对应的用户信息，同时获取当前时间信息，
    username = request.session.get('username')
    userInfo = User.objects.get(username=username)
    year, mon, day = getHomeData.getNowTime()
    # 然后调用 getPublicData 模块中的 getAllTravelInfoMapData 函数获取所有旅游信息数据，
    talbeData = getPublicData.getAllTravelInfoMapData()
    # 最后将用户信息、当前时间信息以及旅游信息数据传递给 tableData.html 模板进行渲染展示，大概率是在页面上以表格形式呈现旅游信息内容给用户查看。
    return render(request, 'tableData.html', {
        'userInfo': userInfo,
        'nowTime': {
            'year': year,
            'mon': getPublicData.monthList[mon - 1],
            'day': day
        },
        'talbeData':talbeData
    })

def addComments(request,id):
    # 先获取用户名、对应的用户信息以及当前时间信息，
    username = request.session.get('username')
    userInfo = User.objects.get(username=username)
    year, mon, day = getHomeData.getNowTime()
    # 同时根据传入的 id 参数调用 getAddCommentsData 模块中的 getTravelById 函数获取对应的旅游信息对象。
    travelInfo = getAddCommentsData.getTravelById(id)
    # 当请求方法为 POST 时，表示用户提交了添加评论的表单，从表单中获取评论的评分（rate）和内容（content）信息，
    # 然后将包含评论相关的各种信息（如评论所属用户信息、对应的旅游信息、评论的评分和内容等）的字典作为参数传递给
    # getAddCommentsData 模块的 addComments 函数，用于将评论数据保存到数据库等相关操作，
    if request.method == 'POST':
        getAddCommentsData.addComments({
            'id':id,
            'rate':int(request.POST.get('rate')),
            'content':request.POST.get('content'),
            'userInfo':userInfo,
            'travelInfo':travelInfo
        })
        # 操作完成后重定向到 tableData 页面（/app/tableData），是方便用户查看添加评论后的旅游信息情况。
        return redirect('/app/tableData')
    # 若请求为 GET 请求，则渲染 addComments.html 模板，并将用户信息、当前时间信息、旅游信息以及 id 参数传递给模板进行展示，
    # 用户可在该页面填写评论内容并提交。
    return render(request, 'addComments.html', {
        'userInfo': userInfo,
        'nowTime': {
            'year': year,
            'mon': getPublicData.monthList[mon - 1],
            'day': day
        },
        'travelInfo':travelInfo,
        'id':id
    })

def cityChar(request):
    # 获取用户名、用户信息以及当前时间信息后，
    username = request.session.get('username')
    userInfo = User.objects.get(username=username)
    year, mon, day = getHomeData.getNowTime()
    # 调用 getEchartsData 模块中的 cityCharDataOne 函数获取用于生成城市相关图表的一组数据（Xdata 和 Ydata），
    Xdata,Ydata = getEchartsData.cityCharDataOne()
    # 再调用 getEchartsData 模块的 cityCharDataTwo 函数获取另一组相关数据（resultData），
    resultData = getEchartsData.cityCharDataTwo()
    # 最后将用户信息、当前时间信息以及两组图表数据整理成字典形式传递给 cityChar.html 模板进行渲染展示，
    # 在页面上通过 Echarts 等图表库展示城市相关的数据可视化内容给用户。
    return render(request, 'cityChar.html', {
        'userInfo': userInfo,
        'nowTime': {
            'year': year,
            'mon': getPublicData.monthList[mon - 1],
            'day': day
        },
        'cityCharOneData':{
            'Xdata':Xdata,
            'Ydata':Ydata
        },
        'cityCharTwoData':resultData
    })

def rateChar(request):
    # 先获取用户名、用户信息和当前时间信息，
    username = request.session.get('username')
    userInfo = User.objects.get(username=username)
    year, mon, day = getHomeData.getNowTime()
    # 接着调用 getPublicData 模块的 getCityList 函数获取城市列表信息，
    cityList = getPublicData.getCityList()
    # 然后以城市列表中的第一个城市为参数调用 getPublicData 模块的 getAllTravelInfoMapData 函数获取对应城市的旅游信息列表，
    travelList = getPublicData.getAllTravelInfoMapData(cityList[0])
    # 再基于该旅游信息列表分别调用 getEchartsData 模块中的 getRateCharDataOne 和 getRateCharDataTwo
    # 函数获取两组用于生成评分相关图表的数据（charOneData 和 charTwoData）。
    charOneData = getEchartsData.getRateCharDataOne(travelList)
    charTwoData = getEchartsData.getRateCharDataTwo(travelList)
    # 当请求方法为 POST 时，说明用户可能通过表单选择了不同的省份，此时根据用户选择的省份重新获取相应的旅游信息列表，
    # 并再次调用上述两个函数获取更新后的图表数据。
    if request.method == 'POST':
        travelList = getPublicData.getAllTravelInfoMapData(request.POST.get('province'))
        charOneData = getEchartsData.getRateCharDataOne(travelList)
        charTwoData = getEchartsData.getRateCharDataTwo(travelList)


    return render(request, 'rateChar.html', {
        'userInfo': userInfo,
        'nowTime': {
            'year': year,
            'mon': getPublicData.monthList[mon - 1],
            'day': day
        },
        'cityList':cityList,
        'charOneData':charOneData,
        'charTwoData':charTwoData
    })
# 该函数主要用于处理价格相关图表数据展示的页面逻辑。它从用户会话中获取当前登录用户信息，获取当前时间，获取城市列表以及所有旅游信息数据，
# 然后调用相关函数获取不同维度的价格相关图表数据，最后将这些数据传递给对应的 HTML 模板进行渲染展示，以向用户呈现价格方面的可视化分析内容。
def priceChar(request):
    # 首先通过 request.session.get('username') 从当前会话中获取用户名，
    username = request.session.get('username')
    # 再利用 User.objects.get(username=username) 根据用户名从数据库中获取对应的用户信息对象 userInfo，
    userInfo = User.objects.get(username=username)
    # 接着调用 getHomeData.getNowTime() 获取当前的年、月、日信息，分别赋值给 year、mon、day。
    year, mon, day = getHomeData.getNowTime()
    # 调用 getPublicData.getCityList() 获取城市列表数据，存储在 cityList 变量中，
    cityList = getPublicData.getCityList()
    # 通过 getPublicData.getAllTravelInfoMapData() 获取所有的旅游信息数据列表，赋值给 travelList，这是后续生成价格图表数据的基础数据来源
    travelList = getPublicData.getAllTravelInfoMapData()
    # 分别调用 getEchartsData.getPriceCharDataOne(travelList)、getEchartsData.getPriceCharDataTwo(travelList)
    # 和 getEchartsData.getPriceCharDataThree(travelList) 这三个函数，基于前面获取的旅游信息列表 travelList
    # 来获取三组不同的用于价格相关图表展示的数据，分别存储在对应的变量 xData 与 yData、x1Data 与 y1Data 以及 disCountPieData 中。
    # 这些函数具体如何从旅游信息中提取和整理数据来生成适合图表展示的数据结构,但整体目的是为了生成价格相关的可视化数据。
    xData,yData = getEchartsData.getPriceCharDataOne(travelList)
    x1Data,y1Data = getEchartsData.getPriceCharDataTwo(travelList)
    disCountPieData = getEchartsData.getPriceCharDataThree(travelList)
    # 最后通过 render 函数将 userInfo（用户信息）、nowTime（当前时间信息，以字典形式包含年、月、日）、cityList（城市列表）以及整合后的
    # echartsData（包含三组价格图表相关数据的字典）作为上下文数据传递给 priceChar.html 模板进行渲染展示，在页面上利用 JavaScript 图表库
    # （Echarts 等）将这些数据绘制成相应的价格相关图表呈现给用户
    return render(request, 'priceChar.html', {
        'userInfo': userInfo,
        'nowTime': {
            'year': year,
            'mon': getPublicData.monthList[mon - 1],
            'day': day
        },
        'cityList': cityList,
        'echartsData':{
            'xData':xData,
            'yData':yData,
            'x1Data':x1Data,
            'y1Data':y1Data,
            'disCountPieData':disCountPieData
        }
    })
# 此函数旨在处理与用户评论相关的数据展示逻辑，为用户呈现评论相关的可视化图表内容。它同样先获取当前登录用户信息和时间信息，
# 然后调用多个函数获取不同角度的评论相关图表数据，最后把这些数据传递给对应的 HTML 模板进行渲染展示，帮助用户直观地了解评论的相关情况。
def commentsChar(request):
    # 获取用户及时间信息：和前面的函数类似，
    username = request.session.get('username')
    userInfo = User.objects.get(username=username)
    year, mon, day = getHomeData.getNowTime()
    # 依次调用 getEchartsData.getCommentsCharDataOne()、getEchartsData.getCommentsCharDataTwo() 和
    # getEchartsData.getCommentsCharDataThree() 函数来获取三组不同的评论相关图表数据，分别赋值给对应的变量。以用于生成合适的可视化图表。
    xData,yData = getEchartsData.getCommentsCharDataOne()
    commentsScorePieData = getEchartsData.getCommentsCharDataTwo()
    x1Data,y1Data = getEchartsData.getCommentsCharDataThree()
    # 通过 render 函数将用户信息、当前时间信息以及整合后的包含三组评论图表数据的 echartsData 字典作为上下文数据传递给
    # commentsChar.html 模板，在页面上借助图表库将这些数据展示为相应的图表，
    return render(request, 'commentsChar.html', {
        'userInfo': userInfo,
        'nowTime': {
            'year': year,
            'mon': getPublicData.monthList[mon - 1],
            'day': day
        },
        'echartsData':{
            'xData':xData,
            'yData':yData,
            'commentsScorePieData':commentsScorePieData,
            'x1Data':x1Data,
            'y1Data':y1Data
        }
    })
# 该函数核心功能是实现旅游推荐功能的页面逻辑处理，尝试根据用户的历史评分数据通过协同过滤算法来推荐旅游景点，
# 如果在获取评分数据或推荐计算过程中出现异常，则随机推荐旅游景点，最后将推荐的旅游景点信息传递给对应的 HTML 模板进行展示，
# 为用户提供个性化或随机的旅游推荐内容。
def recommendation(request):
    #
    username = request.session.get('username')
    userInfo = User.objects.get(username=username)
    year, mon, day = getHomeData.getNowTime()
    # 调用 getUser_ratings() 函数获取用户对旅游景点的评分数据，这个函数应该是从数据库中提取并整理用户的评分信息，返回一个合适的数据结构
    # 存储在 user_ratings 变量中。
    try:
        user_ratings = getUser_ratings()
        # 接着调用 user_bases_collaborative_filtering(userInfo.id, user_ratings) 函数，传入当前用户的 ID（通过 userInfo.id 获取）
        # 以及前面获取的用户评分数据，该函数基于用户的协同过滤算法来计算并推荐适合当前用户的旅游景点，
        # 返回推荐的景点标识列表存储在 recommended_items 变量中。
        recommended_items = user_bases_collaborative_filtering(userInfo.id, user_ratings)
        # 再调用 getRecommendationData.getAllTravelByTitle(recommended_items) 函数，根据推荐的景点标识去获取对应的详细旅游信息列表，
        # 存储在 resultDataList 变量中，这样最终得到的就是基于用户历史行为推荐的详细旅游景点信息列表，可用于展示给用户查看。
        resultDataList = getRecommendationData.getAllTravelByTitle(recommended_items)
    # 调用 getRecommendationData.getRandomTravel() 函数获取随机的旅游景点信息列表赋值给 resultDataList，作为一种兜底的推荐策略，
    # 确保页面能展示一定的推荐内容给用户。
    except:
        resultDataList = getRecommendationData.getRandomTravel()
    # 最后使用 render 函数将用户信息、当前时间信息以及包含推荐旅游景点详细信息的 resultDataList 传递给 recommendation.html 模板
    # 进行渲染展示，在页面上向用户呈现推荐的旅游景点内容，方便用户了解并选择感兴趣的旅游目的地。
    return render(request, 'recommendation.html', {
        'userInfo': userInfo,
        'nowTime': {
            'year': year,
            'mon': getPublicData.monthList[mon - 1],
            'day': day
        },
        'resultDataList':resultDataList
    })
# 该函数主要负责渲染展示旅游景点详细介绍信息的词云页面。它获取当前登录用户信息和时间信息后，直接将这些数据传递给对应的 HTML 模板，
# 由模板去进一步处理展示逻辑，页面大概率会展示旅游景点详细介绍文本内容生成的词云图，帮助用户快速抓住景点介绍的重点内容。
def detailIntroCloud(request):
    #
    username = request.session.get('username')
    userInfo = User.objects.get(username=username)
    year, mon, day = getHomeData.getNowTime()
    # 使用 render 函数将用户信息和当前时间信息以字典形式传递给 detailIntroCloud.html 模板进行渲染展示。
    return render(request, 'detailIntroCloud.html', {
        'userInfo': userInfo,
        'nowTime': {
            'year': year,
            'mon': getPublicData.monthList[mon - 1],
            'day': day
        }
    })
# 与 detailIntroCloud 函数类似，此函数主要用于渲染展示用户评论内容的词云页面。它先获取当前登录用户信息和时间信息，然后传递给对应的 HTML 模板，
# 模板会进一步处理展示评论内容生成的词云图，方便用户直观了解用户评论的重点和核心内容。
def commentContentCloud(request):
    username = request.session.get('username')
    userInfo = User.objects.get(username=username)
    year, mon, day = getHomeData.getNowTime()
    # 利用 render 函数将用户信息和当前时间信息传递给 commentContentCloud.html 模板进行渲染展示。
    return render(request, 'commentContentCloud.html', {
        'userInfo': userInfo,
        'nowTime': {
            'year': year,
            'mon': getPublicData.monthList[mon - 1],
            'day': day
        }
    })